package com.fyl.leetcode.BFS;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @author:fyl
 * @date 2021/5/15 8:55
 * @Modified By:
 * @Modified Date:
 * @Description:
 * 给你一个 n x n 的二进制矩阵 grid 中，返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径，返回 -1 。
 *
 * 二进制矩阵中的 畅通路径 是一条从 左上角 单元格（即，(0, 0)）到 右下角 单元格（即，(n - 1, n - 1)）的路径，该路径同时满足下述要求：
 *
 * 路径途经的所有单元格都的值都是 0 。
 * 路径中所有相邻的单元格应当在 8 个方向之一 上连通（即，相邻两单元之间彼此不同且共享一条边或者一个角）。
 * 畅通路径的长度 是该路径途经的单元格总数。
 * 输入：grid = [[0,0,0],[1,1,0],[1,1,0]]
 * 输出：4
 */
public class ShortestPath {
    public static int shortestPathBinaryMatrix(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return -1;  //当grid无意义时，返回-1
        }
        if (grid[0][0] == 1 || grid[grid.length - 1][grid[0].length - 1] == 1) {
            return -1;  //当某个初始角落为1时，返回-1
        }
        // 定义好8个方向
        int[][] direction = new int[][]{{-1, 0}, {-1, -1}, {-1, 1}, {0, 1}, {0, -1}, {1, 0}, {1, -1}, {1, 1}};
        int row = grid.length;
        int column = grid[0].length;
        int pathLen = 0; // 记录路径长度
        Queue<int[]> queue = new LinkedList<>(); //bfs标配套路，队列
        queue.add(new int[]{0, 0});  //出发点压入队列
        grid[0][0] = 1;  // 因为从出发点开始找寻路径，所以出发点不能再被经过，将其设为1
        while (!queue.isEmpty()) { //当队列不空时
            int size = queue.size();
            pathLen++; // 一轮队列遍历完以后，即节点周围的一层节点被遍历完以后，路径长度+1
            while (size-- > 0) { // 依次取出队列的每个节点并进行遍历
                int[] cur = queue.poll();
                if (cur[0] == row - 1 && cur[1] == column - 1) {
                    //如果当前取出节点已经是右下角，直接返回路径长度,否则,继续搜索
                    return pathLen;
                }
                for (int[] dir : direction) { // 对于当前节点，对其8个方向进行搜寻
                    int r = cur[0] + dir[0];  //当前节点的某个方向上的节点坐标
                    int c = cur[1] + dir[1];
                    if (r < 0 || r >= row || c < 0 || c >= column || grid[r][c] == 1) {
                        // 如果搜寻范围越界或者被堵住或者被已经被经历过，则跳过该方向，对下一个方向进行搜索
                        // grid[row][col] 为1有两种情况：
                        // 1.网格初始化时就被设置为1，无法被经历
                        // 2.在之前的某次搜索过程中已经被经历过，则不用再次经历它
                        continue;
                    }
                    grid[r][c] = 1; // 某方向的节点被经历过后，将其标记为1，以后不再经过它
                    queue.add(new int[]{r, c});   //并将该节点加入队列，用于下一层的节点的搜索
                }
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[][] test = new int[][]{{0,1},{1,0}};
        System.out.println(shortestPathBinaryMatrix(test));
    }
}
